﻿/******************************************************************
 *Company: http://www.xiaomutech.com/
 *fileName : qlyricwidget.cpp --- QLyricWidget
 *Auth       : yhni (QQ:393320854)
 *Create    : 2022/9/15
 *Description   :
 *Histroy:
 *<Auth>    <Date>        <Ver>        <Content>
 *         2022/9/15
 *******************************************************************/
#include "qlyricwidget.h"

#include <QFile>
#include <QTextStream>
#include <QApplication>
#include <QRegExp>

#include <QDesktopWidget>
#include <QScreen>
#include <QMouseEvent>
#include <QPainter>
#include <QScrollBar>
#include <QDebug>
#include <QTextCodec>
#include <QScroller>

QImage blurred(const QImage& image, const QRect& rect, int radius, bool alphaOnly = false)
{
    int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
    int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius - 1];

    QImage result = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
    int r1 = rect.top();
    int r2 = rect.bottom();
    int c1 = rect.left();
    int c2 = rect.right();

    int bpl = result.bytesPerLine();
    int rgba[4];
    unsigned char* p;

    int i1 = 0;
    int i2 = 3;

    if (alphaOnly)
        i1 = i2 = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);

    for (int col = c1; col <= c2; col++) {
        p = result.scanLine(r1) + col * 4;
        for (int i = i1; i <= i2; i++)
            rgba[i] = p[i] << 4;

        p += bpl;
        for (int j = r1; j < r2; j++, p += bpl)
            for (int i = i1; i <= i2; i++)
                p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
    }

    for (int row = r1; row <= r2; row++) {
        p = result.scanLine(row) + c1 * 4;
        for (int i = i1; i <= i2; i++)
            rgba[i] = p[i] << 4;

        p += 4;
        for (int j = c1; j < c2; j++, p += 4)
            for (int i = i1; i <= i2; i++)
                p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
    }

    for (int col = c1; col <= c2; col++) {
        p = result.scanLine(r2) + col * 4;
        for (int i = i1; i <= i2; i++)
            rgba[i] = p[i] << 4;

        p -= bpl;
        for (int j = r1; j < r2; j++, p -= bpl)
            for (int i = i1; i <= i2; i++)
                p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
    }

    for (int row = r1; row <= r2; row++) {
        p = result.scanLine(row) + c2 * 4;
        for (int i = i1; i <= i2; i++)
            rgba[i] = p[i] << 4;

        p -= 4;
        for (int j = c1; j < c2; j++, p -= 4)
            for (int i = i1; i <= i2; i++)
                p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
    }

    return result;
}

QLyricWidget::QLyricWidget(QWidget *parent) : QAbstractListWidget(parent)
{
    this->setMouseTracking(true);
    this->setFrameShape(QFrame::NoFrame);
    this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    QScroller::grabGesture(this, QScroller::LeftMouseButtonGesture);
    connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjustSize()));

    m_animation = new QPropertyAnimation(this, "offset");
    m_animation->setLoopCount(1);
    m_animation->setDuration(300);

    m_backgroundColor = "#55000000";
    m_textColor       = "#ffffff";

    m_itemSpace = 0;
    m_nTimeoffset  = 0;
    m_isAmbiguous = true;

    m_desktopLyric = new QDesktopLyric();
    m_desktopLyric->reject();
}

QLyricWidget::~QLyricWidget()
{
    closeDesktop();
}

bool QLyricWidget::setLyric(const QString &_fileName)
{
    clear();
    QString strLyric = qApp->applicationDirPath() + "/lyrics/" + _fileName + ".lrc";
    QFile file(strLyric);
    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << "file open failed" << strLyric;
        return false;
    }

    QTextStream ts(&file);
    ts.setCodec("UTF8");

    qint64 prevMsec = 0;
    qint64 offset =0;
    qint64 msec = 0;
    QString strText;
    QRegExp regExpTitle("\\[(ar)?(ti)?(al)?(by)?(offset)?\\:");
    QRegExp regExpLine("\\[(\\d+):(\\d+).(\\d+)\\]\\S+");
    int index = 0;
    while (!ts.atEnd()) {
        QString line = ts.readLine();
        if (-1 != regExpTitle.indexIn(line))
        {
             strText = line;
            msec = 0;
        }
        else if (-1 != regExpLine.indexIn(line))
        {
            msec = regExpLine.cap(1).toInt() * 60 + regExpLine.cap(2).toInt();
            msec *= 1000;
            msec += regExpLine.cap(3).toInt() * 10;

            strText = line.mid(10);
        }
        else {
            continue;
        }

        if (0 != prevMsec)
        {
            offset = msec - prevMsec - 30;
        }

        this->addItem(new QLyricLine(index, msec, offset, strText));
        prevMsec = msec;
        index++;
    }

    file.close();

    return true;
}

void QLyricWidget::setPosition(qint64 _msec)
{
    bool isMove = false;
    foreach(QAbstractListItem *_item, m_items)
    {
        QLyricLine *lyric = qobject_cast<QLyricLine*>(_item);
        if (lyric->millisecond() > (_msec + m_nTimeoffset))
        {
            if (m_currentIndex != lyric->id() - 1)
            {
            m_currentIndex = lyric->id() - 1;
                QLyricLine *temp = qobject_cast<QLyricLine*>(m_items.value(m_currentIndex));
                m_desktopLyric->setLyricText(temp->text(), temp->staytime());
                isMove = true;
            }
            break;
        }
    }

    if (!isMove) return;

    int pageCnt = m_currentIndex - (this->height() / m_itemSize + 1) / 2;
    pageCnt = pageCnt < 0 ? 0 : pageCnt;
    int value = pageCnt * m_itemSize;

    m_animation->stop();
    m_animation->setStartValue(verticalScrollBar()->value());
    m_animation->setEndValue(value);
    m_animation->start();
}

void QLyricWidget::setMediaState(bool isPause)
{
    m_desktopLyric->setStatus(isPause);
}

int QLyricWidget::offset() const
{
    return m_nTimeoffset;
}

void QLyricWidget::setOffset(int offset)
{
    m_nTimeoffset = offset;
    verticalScrollBar()->setValue(offset);
}

void QLyricWidget::closeDesktop()
{
    if (NULL != m_desktopLyric)
    {
        m_desktopLyric->close();
        delete m_desktopLyric;
        m_desktopLyric = NULL;
    }
}

void QLyricWidget::showDesktopLyric(bool bOk)
{
    m_desktopLyric->setVisible(bOk);
}

void QLyricWidget::setAlbumPixmap(const QImage &_img)
{
    m_imageBackground = _img;
    m_imageAlbum = blurred(_img, QRect(0, 0, _img.width(), _img.height()), 100);
    viewport()->update();
}

void QLyricWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(viewport());
    painter.setRenderHints(QPainter::Antialiasing);
    if (!m_imageAlbum.isNull())
    {
        painter.drawImage(this->rect(),  m_isAmbiguous ? m_imageAlbum : m_imageBackground);
    }
    painter.fillRect(viewport()->rect(), m_backgroundColor);
    painter.setPen(m_textColor);

    if (m_items.isEmpty())
    {
        painter.drawText(viewport()->rect(), Qt::AlignCenter, m_strEmptyHint);
    }
    foreach(QAbstractListItem *_item, m_items) {
        if (_item->rect().width() > 0 && _item->rect().bottom() > 0) {
            drawItemInfo(&painter, _item);
        }
    }
}

void QLyricWidget::drawItemInfo(QPainter *painter, QAbstractListItem *_item)
{
    painter->save();
    QLyricLine *lyric = qobject_cast<QLyricLine*>(_item);
    painter->setPen(m_currentIndex == _item->id() ? QColor("#1ecc94") : m_textColor);
    QFont font = this->font();
    int pixSize = font.pixelSize();
    font.setPixelSize(m_currentIndex == _item->id() ? (pixSize + 5) : pixSize);
    painter->setFont(font);
    painter->drawText(lyric->rect(), Qt::AlignCenter, lyric->text());
    painter->restore();
}

void QLyricWidget::resizeEvent(QResizeEvent *e)
{
    QFont font = this->font();
    int pixSize = font.pixelSize();
    font.setPixelSize(pixSize + 5);
    QFontMetrics fm(font);
    m_itemSize = fm.height() + 10;
    QAbstractListWidget::resizeEvent(e);
}

/* 拦截鼠标事件 */
void QLyricWidget::mousePressEvent(QMouseEvent *e)
{
    Q_UNUSED(e)
}

void QLyricWidget::mouseDoubleClickEvent(QMouseEvent *e)
{
    m_isAmbiguous = !m_isAmbiguous;
    viewport()->update();
    QAbstractListWidget::mouseDoubleClickEvent(e);
}

///////////////////////////////////////////////////////////////////
QLyricLine::QLyricLine(int _id, qint64 _msec, qint64 _stay, const QString &_text) :
    QAbstractListItem(_id),
    m_millisecond(_msec), m_staytime(_stay), m_lineText(_text)
{

}

qint64 QLyricLine::millisecond()
{
    return m_millisecond;
}

qint64 QLyricLine::staytime()
{
    return m_staytime > 8000 ? 3000 : m_staytime;
}

QString QLyricLine::text() const
{
    return m_lineText;
}

///////////////////////////////////////////////////////////////////
QDesktopLyric::QDesktopLyric(QWidget *parent) : QDialog(parent)
{
    this->setFixedSize(700, 80);
    setAttribute(Qt::WA_TranslucentBackground, true);
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool);
    this->setCursor(Qt::PointingHandCursor);

    m_linearGradient.setStart(0, 10);
    m_linearGradient.setFinalStop(0, 40);
    m_linearGradient.setColorAt(0.1, QColor(14, 179, 255));
    m_linearGradient.setColorAt(0.5, QColor(114, 32, 255));
    m_linearGradient.setColorAt(0.9, QColor(14, 179, 255));

    //已经播过的
    m_maskGradient.setStart(0, 10);
    m_maskGradient.setFinalStop(0, 40);
    m_maskGradient.setColorAt(0.1, QColor(222, 54, 4));
    m_maskGradient.setColorAt(0.5, QColor(255, 72, 16));
    m_maskGradient.setColorAt(0.9, QColor(222, 54, 4));

    m_strText = QStringLiteral("酷我音乐，听我想听！");
    m_textFont = QFont("Microsoft Yahei");
    m_textFont.setPixelSize(36);
    m_maskWidth = 0;

    m_animation = new QPropertyAnimation(this, "mask");
    m_animation->setLoopCount(1);

    m_btnClose = QRect(660, 10, 30, 30);
}

QDesktopLyric::~QDesktopLyric()
{

}

void QDesktopLyric::setLyricText(const QString &_text, qint64 _staytime)
{
    m_strText = _text;
    QFontMetrics fm(m_textFont);
#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)
    int txtW = fm.horizontalAdvance(m_strText);
#else
    int txtW = fm.width(m_strText);
#endif
    if (txtW > 0 && _staytime > 0 && this->isVisible())
    {
        m_animation->stop();
        m_animation->setStartValue(0);
        m_animation->setEndValue(txtW);
        m_animation->setDuration(_staytime);
        m_animation->start();
    }

    this->update();
}

int QDesktopLyric::maskWidth() const
{
    return m_maskWidth;
}

void QDesktopLyric::setMaskWidth(int maskWidth)
{
    m_maskWidth = maskWidth;
    this->update();
}

void QDesktopLyric::setStatus(bool isPause)
{
    if (isPause && m_animation->state() == QPropertyAnimation::Running)
    {
        m_animation->setPaused(true);
    }
}

void QDesktopLyric::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    if (m_isHover)
    {
        painter.fillRect(rect(), QColor(50, 50, 50, 153));
        drawIconfont(&painter, m_btnClose, QChar(0xe646), "#fafafa", 20);
    }

    painter.setFont(m_textFont);
#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)
    int txtW = painter.fontMetrics().horizontalAdvance(m_strText);
#else
    int txtW = painter.fontMetrics().width(m_strText);
#endif
    int startX = (this->width() - txtW) / 2;
    QRect rectText(startX, 0, txtW, this->height());

    painter.setPen(Qt::black);
    painter.drawText(rectText, Qt::AlignVCenter, m_strText);

    painter.setPen(QPen(m_linearGradient, 0));
    painter.drawText(rectText, Qt::AlignVCenter, m_strText);

    painter.setPen(QPen(m_maskGradient, 0));
    rectText = QRect(startX, 0, m_maskWidth, this->height());
    painter.drawText(rectText, Qt::AlignVCenter, m_strText);
}

void QDesktopLyric::drawIconfont(QPainter *painter, const QRect &_rect, QChar _ico, QColor _color, int _size)
{
    painter->save();
    QFont font("iconfont");
    font.setPixelSize(_size);
    painter->setFont(font);
    painter->setPen(_color);
    painter->drawText(_rect, Qt::AlignCenter, _ico);
    painter->restore();
}

void QDesktopLyric::resizeEvent(QResizeEvent *e)
{
    QSize size = qApp->primaryScreen()->geometry().size();
    int x = size.width() - this->width() - 20;
    int y = size.height() - this->height() - 50;
    this->move(x, y);
    QDialog::resizeEvent(e);
}

void QDesktopLyric::mouseMoveEvent(QMouseEvent *e)
{
    if (m_isPressed)
    {
        this->move(e->globalPos() - m_startPos);
    }
}

void QDesktopLyric::mousePressEvent(QMouseEvent *e)
{
    if (e->y() < 30)
    {
        m_isPressed = true;
        m_startPos = e->globalPos() - this->pos();
    }

    if (m_btnClose.contains(e->pos()))
    {
        this->reject();
    }
}

void QDesktopLyric::mouseReleaseEvent(QMouseEvent *)
{
    m_isPressed = false;
}

void QDesktopLyric::enterEvent(QEvent *e)
{
    m_isHover = true;
    this->update();
    QDialog::enterEvent(e);
}

void QDesktopLyric::leaveEvent(QEvent *e)
{
    m_isHover = false;
    this->update();
    QDialog::leaveEvent(e);
}

////////////////////////////////////////////////////////////////////////
QDesktopLyricPrev::QDesktopLyricPrev(QWidget *parent) : QWidget(parent)
{
    m_strText = QStringLiteral("酷我音乐 听你想听");
    m_textFont = QFont("Microsoft Yahei");
    m_textFont.setPixelSize(36);
    m_linearGradient = "#00a1ec";
    m_maskGradient = "#e6000b";
}

void QDesktopLyricPrev::setText(const QString &_text)
{
    m_strText = _text;
    adjuestSize();
}

void QDesktopLyricPrev::setTextFont(QFont font)
{
    m_textFont = font;
    adjuestSize();
}

void QDesktopLyricPrev::setLyricColor(const QColor &_text, const QColor &_mask)
{
    m_linearGradient = _text;
    m_maskGradient = _mask;
    this->update();
}

void QDesktopLyricPrev::adjuestSize()
{
    QFontMetrics fm(m_textFont);
#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)
    int txtW = fm.horizontalAdvance(m_strText);
#else
    int txtW = fm.width(m_strText);
#endif
    m_maskWidth = txtW / 2;
    this->update();
}

void QDesktopLyricPrev::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.fillRect(this->rect(), QColor("#eeeeee"));

    painter.setFont(m_textFont);
#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)
    int txtW = painter.fontMetrics().horizontalAdvance(m_strText);
#else
    int txtW = painter.fontMetrics().width(m_strText);
#endif
    int startX = (this->width() - txtW) / 2;
    QRect rectText(startX, 0, txtW, this->height());

    painter.setPen(Qt::black);
    painter.drawText(rectText, Qt::AlignVCenter, m_strText);

    painter.setPen(QPen(m_linearGradient, 0));
    painter.drawText(rectText, Qt::AlignVCenter, m_strText);

    painter.setPen(QPen(m_maskGradient, 0));
    rectText = QRect(startX, 0, txtW * 0.5, this->height());
    painter.drawText(rectText, Qt::AlignVCenter, m_strText);
}
